#--
#   Television Episode Downloader
#   - Initial Milestone: 4-02-08
#       - Find URL from epguides.com
#       - filenames to rename_list.csv
#   - Internal Milestone 1: 4-05-08
#       - construct new name based on Template
#       - correctly interact with rename_list, make batch/sh/etc and better
#   - Internal Milestone 2: 4-06-08
#       - search an epguides.txt file and html page and fill out ep title
#   - Internal Milestone 3: 4-08-08
#       - option to walk directory structure from given location
#       - optparse add and support
#   - Eventual Goals
#       - Nice to Have
#           - create html page with each file's link to recap
#           - that page has link to dvd/show site image, saved to dir as folder.jpg
#       - Needs
#           - Try and pull series name from file name first, vet it somehow
#       
#
#   Rob Dennis - Lurchington Q. Delacroix ESQ. and man about town
#
#   This script is inspired and is thanks to:
#   -   Rob Meerman's tvrenamer perl script
#           http://www.robmeerman.co.uk/coding/file_renamer
#   -   Softpointer's Tag&Rename
#           http://www.softpointer.com/tr
#   
#--

import os,re,glob,time,sys,string,shutil,urllib2
from string import Template

from optparse import OptionParser

parser = OptionParser(version="%prog - Internal Mileston 3 - April 8th, 2008")

parser.add_option("--makeRenameBatchFile",dest="makeRenameBatchFlag",action="store_true",
                  help="enable creation of batch file",default=False)

parser.add_option("--quashBatchEntries",dest="quashBatchEntriesFlag",action="store_true",
                  help="always overwrite batch file",default=False)

parser.add_option("--pretend",dest="pretendFlag",action="store_true",
                  help="do not make any changes",default=True)

parser.add_option("--assumeGroupedNum",dest="assumeGroupedNumFlag",action="store_true",
                  help="treats fields like 1005 as both season and episode number",default=True)

parser.add_option("--groupNumSplit",dest="groupNumSplit",metavar="NUM",
                  help="when handling group numbers, split after NUM places [default: %default]",default=2)

parser.add_option("--episodePadding",dest="episodePadding",metavar="NUM",
                  help="pads episode number to NUM places [default: %default]",default=2)

parser.add_option("--useUnixNaming",dest="useUnixNamingFlag",action="store_true",
                  help="replaces whitespace with '_'",default=False)

parser.add_option("--recursiveRename",dest="recursiveRenameFlag",action="store_true",
                  help="recursively rename files in all subdirectory",default=True)

parser.add_option("--debug",dest="debugFlag",action="store_true",
                  help="print extra debug output",default=True)

parser.add_option("--verbose",dest="verboseFlag",action="store_true",
                  help="print verbose output",default=True)

parser.add_option("--topLevel",dest="topLevelDir",metavar="PATH",
                  help="top level rename hierarchy [default: %default]",default=os.getcwd())

parser.add_option("--renameString",dest="renameString",metavar="STRING",
                  help="rename filename to formatted STRING [default: %default]",
                  default=r"${showName} s${seasonNum}e${episodeNum}-${episodeTitle}")


epguidesTxt= {"isFile":True,"filename":"epguides.txt","syntax":"epguidesTxt"}
tvAllTxt = {"isFile":True,"filename":"tv.txt","syntax":"tvComTxt"}
epguidesHTML= {"isSite":True,"URL":r"http://www.epguides.com/","syntax":"epguidesHTML"}

(opt,arg) = parser.parse_args()

informationSources= [epguidesTxt,tvAllTxt,epguidesHTML]

if opt.useUnixNamingFlag:
    whitespaceToUnderscore = string.maketrans(" ","_")

def dPrnt(string,flag=opt.debugFlag,left="<dPrnt-:",right="->"):
    if flag:
        print left+str(string)+right
    
    
def renameFileList(inList):
#loop 1
    row = []
    for x in range(len(inList)):
        row.append(reshapeName(inList[x]))
        if opt.useUnixNamingFlag:
            row[x]=row[x].translate(whitespaceToUnderscore)
        
        if inList[x]!=row[x]:
            dPrnt("executing: mv %s %s" %(inList[x],row[x]))
            if not opt.pretendFlag:
                shutil.move(inList[x],row[x])
        else:
            dPrnt("no changes needed to: %s" %row[x])

    if (opt.makeRenameBatchFlag):
        makeRenameBatchFile("renameBatch.sh","mv",row,inList)


def makeRenameBatchFile(fileName,command,source,dest):
    dPrnt("creating batch file")
    if opt.quashBatchEntriesFlag:
        dPrnt("overwriting current batch")
    if len(source) != len(dest):
        dPrnt("mismatched rename lists!")
    else:
        sourceNot=[]
        destNot=[]
        if os.path.exists(fileName):
            inF=open(fileName,"rb")

            if not opt.quashBatchEntriesFlag:
                modeString="ab"
                for line in inF.readlines():
                    if len(line)>1:
                        if line[0] != "#":
                            lineS=line.split()[1:]
                            if len(lineS)>1 and (lineS[1] in dest):
                                dPrnt(lineS[1]+"matched, dropped from file")
                                x=dest.index(lineS[1])
                                destNot.append(dest[x])
                                sourceNot.append(source[x])
                inF.close()
                
            else:
                modeString="wb"
        elif not pretendMoveFlag:
            modeString="wb"
            os.system("touch %s" %fileName)


        if not pretendMoveFlag:
            outF=open(fileName,modeString)

            if modeString=="wb":
                outF.write("#!/bin/bash\n")            
        
        for x in range(len(source)):
            if not pretendMoveFlag:
                if source[x] not in sourceNot and dest[x] not in destNot:
                    if source[x]!=dest[x]:
                        outF.write("%s %s %s\n" %(command,source[x],dest[x]))
                    else:
                        dPrnt("batch source and dest match for %s" % source[x])
        if not pretendMoveFlag:
            outF.close()
            

def reshapeName(oldName,renameString=opt.renameString):
    nameTemplate=Template(renameString)

    show,season,episode,title=searchInfo(oldName)

    if show!="UNK":
        extension=oldName[oldName.rfind("."):]
        return nameTemplate.substitute(showName=show,seasonNum=str(season),episodeNum=str(episode),episodeTitle=str(title))+extension
    else:
        dPrnt("returning old name due to undefined information")
        return oldName

def searchInfo(fileN):
#- Getting tv show name from folder
    shownameRE1=re.compile(r".*(?=(Season))",re.IGNORECASE)
    shownameRE2=re.compile(r".*(?=([0-9]+x))",re.IGNORECASE)


##    if shownameRE1.search

    parentDir=os.path.split(os.getcwd())[1].strip()
    parentOfParentDir=os.path.split(os.path.split(os.getcwd())[0])[1].strip()

    dPrnt("cwd: %s" % os.getcwd())
    dPrnt("Parent of Parent Dir: %s" % parentOfParentDir)
    dPrnt("Parent Dir: %s" % parentDir)

    re1=re.search(shownameRE1,parentDir,re.I)
    re2=re.search(shownameRE2,parentDir,re.I)

    if re1:
        showname=re1.group().strip()
        dPrnt("RE1: %s" % showname)
    elif re2:
        showname=re2.group().strip()
        dPrnt("RE2: %s" % showname)
    else:
        showname="UNK"
        
#- Getting season number from filename
    seasonRE1=re.compile(r"(?<=[\ _xs\.])[0-9]+",re.IGNORECASE)
    seasonRE2=re.compile(r"^[0-9]+")
    seasonRet="UNK"
    episodeRet="UNK"
    
    if seasonRE1.search(fileN):
        seasonRet=seasonRE1.search(fileN).group(0)
    elif seasonRE2.search(fileN):
        seasonRet=seasonRE2.search(fileN).group(0)
    else:
        dPrnt("no season parsed in %s" % fileN)

    seasonIndex=fileN.find(seasonRet)+1
    
    if seasonRet != "UNK" and opt.assumeGroupedNumFlag:
        if len(seasonRet)>opt.groupNumSplit:
            episodeRet=seasonRet[-opt.groupNumSplit:]
            seasonRet=seasonRet[:-opt.groupNumSplit]            
            
#- Getting episode number from filename
    episodeRE1=re.compile(r"(?<=[\ _xe\.])[0-9]+",re.IGNORECASE)
    episodeRE2=re.compile(r"^[0-9]+")

    if episodeRet == "UNK":
        if episodeRE1.search(fileN):
            episodeRet=episodeRE1.search(fileN[seasonIndex:]).group(0)
        elif episodeRE2.search(fileN):
            episodeRet=episodeRE2.search(fileN[seasonIndex:]).group(0)
        else:
            dPrnt("no episode parsed in %s" % fileN)

    if episodeRet.isdigit():
        episodeRet=episodeRet.zfill(opt.episodePadding)

#- Getting the episde title from a source
    if episodeRet.isdigit() and seasonRet.isdigit() and showname != "UNK":
        for source in informationSources:
            if source.has_key("isFile"):
                if os.path.exists(source["filename"]):
                    title=titleFromFile(source,showname,int(seasonRet),int(episodeRet))
            else:
                dPrnt("trying website: %s" % source["URL"])
                title=titleFromSite(source,showname,int(seasonRet),int(episodeRet))
    else:
        title="UNK"

    dPrnt("showname: [%s] season: [%s] episodeRet: [%s] title: [%s]" %(showname,seasonRet,episodeRet,title))

    return showname,seasonRet,episodeRet,title
        

def titleFromSite(source,showname,season,episode):

    if source["syntax"]=="epguidesHTML":
        norm = string.maketrans("","")
        searchTerm=showname.translate(norm,string.whitespace+"'")
        if re.match("The",searchTerm,re.I):
            searchTerm=searchTerm[3:]
        pullPage=source["URL"]+searchTerm
        dPrnt("searching %s" %pullPage)

    
        if source.has_key(searchTerm):
            sourcePage=source[searchTerm]
            dPrnt(showname+" information pushed to %s" % source["syntax"])
        else:
            try:
                sourcePage=urllib2.urlopen(pullPage).read()
                
            except:
                dPrnt("no page found, continuing search")
                source[searchTerm]=None
                return "UNK"
            dPrnt(showname+"information saved to %s" % source["syntax"])
            source[searchTerm]=sourcePage

        if not sourcePage:
            dPrnt("cached results show unknown show name")
            return "UNK"

    if source["syntax"]=="epguidesHTML":
        sval=str(season).rjust(2)
        epval=str(episode).rjust(2)
        searchTemplate=Template(r"(?<=${seasonT}-${episodeT}.{28}).*")
        searchTemplate=searchTemplate.substitute(seasonT=sval,episodeT=epval)
        titleRE=re.compile(searchTemplate,re.IGNORECASE&re.MULTILINE)
        titleAndLink=titleRE.search(sourcePage).group(0)

        titleFieldRE=re.compile(r"(?<=>)[a-zA-Z0-9 ]*")
        return titleFieldRE.search(titleAndLink).group(0)
##        return titleRE.search(inF.read()).group(0)

    
    
    
    
def titleFromFile(source,showname,season,episode):
    inF=open(source["filename"],"rb")

    if source["syntax"]=="epguidesTxt":
        sval=str(season).rjust(2)
        epval=str(episode).rjust(2)
        searchTemplate=Template(r"(?<=${seasonT}-${episodeT}.{28}).*")
        searchTemplate=searchTemplate.substitute(seasonT=sval,episodeT=epval)
        titleRE=re.compile(searchTemplate,re.IGNORECASE&re.MULTILINE)
        return titleRE.search(inF.read()).group(0)

def main():
    startTime=time.time()
    
    fileTypes = re.compile(r'.*(\.avi|mkv|\.mpg)$',re.IGNORECASE)

    os.chdir(opt.topLevelDir)
    
    for root,dirs,fileNameList in os.walk(opt.topLevelDir):
        dPrnt("checking %s" %root)
        os.chdir(root)
        
        if len(fileNameList)>0:
            newFileNameList = []

            for fileN in fileNameList:
                if fileTypes.match(fileN.strip()):
                    newFileNameList.append(fileN)

            listLength=len(newFileNameList)

            dPrnt("Attempting to rename %s files" % listLength)

            if listLength>0:
                renameFileList(newFileNameList)
                dPrnt(time.time()-startTime)

                prompt=raw_input("continue? (y/n): ")
            
                if prompt=="n":
                    break            
            else:
                dPrnt("no usable files found in %s" % root)

main()
